home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / databasehelper.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  6.1 KB  |  181 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. from itertools import count
  19.  
  20. import database
  21.  
  22. def makeSimpleGetSet(attributeName, changeNeedsSave=True):
  23.     """Creates a simple DDBObject getter and setter for an attribute.
  24.  
  25.     This exists because for many DDBOBject attributes we have methods like the
  26.     following:
  27.  
  28.     def getFoo(self):
  29.         self.confirmDBThread()
  30.         return self.foo
  31.     def setFoo(self, newFoo):
  32.         self.confirmDBThread()
  33.         self.foo = newFoo
  34.         self.signalChange()
  35.     """
  36.  
  37.     def getter(self):
  38.         self.confirmDBThread()
  39.         return getattr(self, attributeName)
  40.     def setter(self, newValue):
  41.         self.confirmDBThread()
  42.         setattr(self, attributeName, newValue)
  43.         self.signalChange(needsSave=changeNeedsSave)
  44.     return getter, setter
  45.  
  46. class TrackedIDList(object):
  47.     """Creates a view that tracks a list of DDBObject IDs.
  48.  
  49.     The view will have the corresponding DDBObject for each ID in the list and
  50.     will be in the same order.
  51.  
  52.     Attributes:
  53.  
  54.     view -- Database view that tracks the list of ids
  55.     """
  56.  
  57.     def __init__(self, db, idList, sortFunc=None):
  58.         """Construct an TrackedIDList.  
  59.  
  60.         This object will keep a reference to idList.  When insertID, appendID,
  61.         removeID, or moveID are called idList will be modified to
  62.         reflect the change.
  63.         """
  64.         if sortFunc == None:
  65.             sortFunc = self.sort
  66.  
  67.         self.trackedIDs = set()
  68.         self.positions = {}
  69.         self.list = idList
  70.         self.db = db
  71.         pos = count()
  72.         for id in idList:
  73.             self.positions[id] = pos.next()
  74.             self.trackedIDs.add(id)
  75.         self.extraFilterFunc = lambda x: True
  76.         self.filter1 = db.filter(self.filter)
  77.         self.filter2 = self.filter1.filter(self.extraFilter)        
  78.         self.view = self.filter2.sort(sortFunc, resort=True)
  79.  
  80.     def recomputeSort(self):
  81.         self.filter2.recomputeSort(self.view)
  82.  
  83.     def sort(self, a, b):
  84.         return self.positions[a[1].getID()] < self.positions[b[1].getID()]
  85.  
  86.     def filter(self, object):
  87.         return object.getID() in self.trackedIDs
  88.  
  89.     def extraFilter(self, object):
  90.         return self.extraFilterFunc(object)
  91.  
  92.     def setFilter(self, extraFilterFunc):
  93.         self.extraFilterFunc = extraFilterFunc
  94.         self.filter1.recomputeFilter(self.filter2)
  95.  
  96.     def _sendSignalChange(self, id):
  97.         if self.db.idExists(id):
  98.             self.db.getObjectByID(id).signalChange(needsSave=False)
  99.  
  100.     def __contains__(self, id):
  101.         return id in self.trackedIDs
  102.  
  103.     def getPosition(self, id):
  104.         """Get the position of an id in the list.  If id is not in the list a
  105.         KeyError will be raised."""
  106.         return self.positions[id]
  107.  
  108.     def appendID(self, id, sendSignalChange=True):
  109.         if id in self:
  110.             raise ValueError("%s is already being tracked" % id)
  111.         self.positions[id] = len(self.list)
  112.         self.trackedIDs.add(id)
  113.         self.list.append(id)
  114.         if sendSignalChange:
  115.             self._sendSignalChange(id)
  116.  
  117.     def insertID(self, pos, id):
  118.         if id in self:
  119.             raise ValueError("%s is already being tracked" % id)
  120.         for toMove, oldPos in self.positions.items():
  121.             if oldPos >= pos:
  122.                 self.positions[toMove] += 1
  123.         self.positions[id] = pos
  124.         self.list.insert(pos, id)
  125.         self.trackedIDs.add(id)
  126.         self._sendSignalChange(id)
  127.  
  128.     def removeID(self, id):
  129.         removedPos = self.positions.pop(id)
  130.         for toMove, oldPos in self.positions.items():
  131.             if oldPos > removedPos:
  132.                 self.positions[toMove] -= 1
  133.         del self.list[removedPos]
  134.         self.trackedIDs.remove(id)
  135.         self._sendSignalChange(id)
  136.  
  137.     def moveID(self, id, pos):
  138.         """Move an id from it's current position to a new position, shifting
  139.         the rest of the list to accomidate it.
  140.         """
  141.  
  142.         currentPos = self.positions[id]
  143.         if currentPos > pos:
  144.             minChange = pos
  145.             maxChange = currentPos-1
  146.             delta = 1
  147.         else:
  148.             minChange = currentPos+1
  149.             maxChange = pos
  150.             delta = -1
  151.         for toMove, oldPos in self.positions.items():
  152.             if minChange <= oldPos <= maxChange:
  153.                 self.positions[toMove] = oldPos + delta
  154.         self.positions[id]  = pos
  155.         del self.list[currentPos]
  156.         self.list.insert(pos, id)
  157.         self._sendSignalChange(id)
  158.  
  159.     def moveIDList(self, idList, anchorID):
  160.         """Move a set of IDs to be above anchorID.
  161.  
  162.         More precicely, we move idList so it is one contiguous block, in
  163.         between anchorID and the first id not in idList.  The ids in idList
  164.         won't change position relative to each other.
  165.  
  166.         If anchorID is None, idList will be positioned at the bottom.
  167.         """
  168.  
  169.         if anchorID in idList:
  170.             return
  171.         toMove = [(self.getPosition(id), id) for id in idList]
  172.         toMove.sort()
  173.         for oldPos, id in toMove:
  174.             self.removeID(id)
  175.         for oldPos, id in toMove:
  176.             if anchorID is not None:
  177.                 anchorPos = self.getPosition(anchorID)
  178.                 self.insertID(anchorPos, id)
  179.             else:
  180.                 self.appendID(id)
  181.